﻿using Renci.SshNet;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Connection;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsForms_测试
{
    public class SSH_Client
    {
        [DllImport("dbgHelp", SetLastError = true)]
        private static extern bool MakeSureDirectoryPathExists(string name);

        static object locker_file = new object();  //写入文件锁

        public bool LogAuto = false;  // 自动记录日志
        public string LogFile = null;
        Thread read; //接受数据线程

        private ShellStream shellstream;
        private SshClient ssh;
        private string _IP = string.Empty;

        public int waitfor_timeout = 10;  //命令执行超时时间

        private object locker = new object();  // 枷锁
                                               //   TextBoxWriter Console;

        ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim(); // 文件写入锁
        private StringBuilder str_data = new StringBuilder();// 所有收到的数据
        private StringBuilder str_Temp = new StringBuilder();//
        private StringBuilder Str_Date_one = new StringBuilder();//单个命令的执行结果,不完整没有启用
        /// <summary>
        /// 接受回调数据的方法
        /// </summary>
        public EventHandler<string> OnReadData;
        /// <summary>
        /// 异常日志回显
        /// </summary>
        public EventHandler<string> OnConsole;

        public string command_resure
        {
            get
            {
                lock (this)
                {
                    return Str_Date_one.ToString();
                }
            }
        }
        public string RecData //取得执行的log
        {
            //get
            //{    return str_data;
            //}
            get
            {
                lock (this)
                {
                    return str_data.ToString();
                }
            }

        }


        /// <summary>
        /// 取得链接状态
        /// </summary>
        public bool Connected
        {
            get
            {
                try
                {

                    if (shellstream == null)
                    {
                        return false;
                    }
                    if (ssh.IsConnected)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                catch
                {
                    return false;
                }

            }
        }
        public bool conneted
        {
            get
            {
                try
                {
                    if (ssh.IsConnected)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                catch
                {
                    return false;
                }

            }
        }

        public bool Conneted
        {
            get
            {
                try
                {
                    if (ssh.IsConnected)
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                }
                catch
                {
                    return false;
                }

            }
        }
        /// <summary>
        /// 打印日志
        /// </summary>
        /// <param name="msg"></param>
        private void OnConsolePrint(string msg)
        {
            if (OnConsole != null)
            {
                OnConsole?.Invoke(null, msg);
            }
            else
            {
                Console.WriteLine(msg);
            }

        }
        public SSH_Client(string Host_Name, string username, string password)
        : this(Host_Name, 22, username, password) { } //构造函数链

        public SSH_Client(string Host_name, int port, string username, string password)
        {
            _IP = Host_name; // IP地址
            try
            {
                ssh = new SshClient(Host_name, 22, username, password);

                ssh.Connect();
                if (ssh.IsConnected)
                {
                    //  shellstream = ssh.CreateShellStream("vt100", 10, 24, 800, 600, 4096);  //
                    shellstream = ssh.CreateShellStream("Xterm", 122, 24, 1220, 600, 4096);  //
                    shellstream.DataReceived += Shellstream_DataReceived;
                    shellstream.ErrorOccurred += Shellstream_ErrorOccurred;
                    shellstream.Closed += Shellstream_Closed;
                    var data = new Renci.SshNet.Common.SshDataStream(98);

                    //Thread read = new Thread(new ThreadStart(Receive));
                    //read.IsBackground = true;
                    //read.Start();
                }
                else
                {
                    Console.WriteLine(_IP + " ssh——链接失败");

                }
            }
            catch { }

        }

        private void Shellstream_Closed(object sender, EventArgs e)
        {

            this.close();
        }
        //  using System.Reflection;

        // 定义发送窗口变化的方法
        public void SendWindowChangeRequest(SshClient client, ShellStream shell, uint columns, uint rows)
        {
            // 获取 ShellStream 的私有 Channel 对象
            var channel = shell.GetType().GetField("_channel", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(shell);

            if (channel == null)
                throw new InvalidOperationException("无法访问 ShellStream 的底层 Channel 对象");

            // 获取远程通道号
            var remoteChannelNumber = (uint)channel.GetType().GetProperty("RemoteChannelNumber").GetValue(channel);

            // 使用 SshDataStream 构建消息
            using (var dataStream = new SshDataStream(64)) // 预分配足够空间
            {
                // SSH_MSG_CHANNEL_REQUEST (98)
                dataStream.WriteByte(98);

                // recipient_channel (远程通道号)
                dataStream.Write(remoteChannelNumber);

                // "window-change" 字符串
                dataStream.Write("window-change", Encoding.ASCII);

                // want_reply (false)
                dataStream.Write(0);

                // columns 和 rows
                dataStream.Write(columns);
                dataStream.Write(rows);

                // 像素值（通常设为0）
                dataStream.Write(0U); // width_pixels
                dataStream.Write(0U); // height_pixels

                // 获取 Session 对象并发送消息
                var session = client.GetType().GetField("_session", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(client);
                if (session == null)
                    throw new InvalidOperationException("无法访问 Session 对象");

                // 通过反射调用 Session.SendMessage
                var sendMessageMethod = session.GetType().GetMethod("SendMessage", BindingFlags.NonPublic | BindingFlags.Instance);
                sendMessageMethod.Invoke(session, new object[] { dataStream.ToArray() });
            }
        }


        public void SendWindowChangeRequest(uint rows, uint columns)
        {
            Console.WriteLine($" {columns} {columns}");
            // 获取 ShellStream 的私有 Channel 对象
            var channel = shellstream.GetType().GetField("_channel", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(shellstream);
            if (channel == null)
                throw new InvalidOperationException("无法访问 ShellStream 的底层 Channel 对象");



            // 获取远程通道号
            var remoteChannelNumber = (uint)channel.GetType().GetProperty("RemoteChannelNumber").GetValue(channel);

            // 使用 SshDataStream 构建消息
            using (var dataStream = new SshDataStream(64)) // 预分配足够空间
            {
                // SSH_MSG_CHANNEL_REQUEST (98)
                dataStream.WriteByte(98);

                // recipient_channel (远程通道号)
                dataStream.Write(remoteChannelNumber);

                // "window-change" 字符串
                dataStream.Write("window-change", Encoding.ASCII);

                // want_reply (false)
                dataStream.Write(0);

                // columns 和 rows
                dataStream.Write(columns);
                dataStream.Write(rows);

                // 像素值（通常设为0）
                dataStream.Write(0U); // width_pixels
                dataStream.Write(0U); // height_pixels




                // 获取 Session 对象并发送消息
                var session = shellstream.GetType().GetField("_session", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(shellstream);
                //if (session == null)
                //    throw new InvalidOperationException("无法访问 Session 对象");

                //// 通过反射调用 Session.SendMessage
                var sendMessageMethod = session.GetType().GetMethod("SendMessage", BindingFlags.NonPublic | BindingFlags.Instance);
                var message = new ChannelRequestMessage();
                message.Load(dataStream.ToArray());
                sendMessageMethod.Invoke(session, new object[] { message });
                //byte[] temp = dataStream.ToArray();

                //shellstream.Write(temp, 0, temp.Length);
                //shellstream.Flush();
            }
        }


        //public void SendWindowChangeRequest1(uint rows, uint columns)
        //{
        //    Console.WriteLine($"Sending window change: {columns}x{rows}");

        //    // 获取 ShellStream 的底层 Channel 对象
        //    var channel = shellstream.GetType().GetField("_channel", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(shellstream);
        //    if (channel == null)
        //        throw new InvalidOperationException("无法访问 ShellStream 的底层 Channel 对象");

        //    // 获取远程通道号
        //    var remoteChannelNumber = (uint)channel.GetType().GetProperty("RemoteChannelNumber").GetValue(channel);

        //    // 构造 SSH 协议消息
        //    var message = new ChannelRequestMessage();
        //    message.
        //        remoteChannelNumber,          // 远程通道号
        //        new ChannelWindowChangeRequest // 窗口变更请求
        //        {
        //            Columns = columns,
        //            Rows = rows,
        //            WidthPixels = 0,
        //            HeightPixels = 0
        //        },
        //        false                          // want_reply
        //    );

        //    // 获取 Session 对象
        //    var session = client.GetType().GetField("_session", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(client);
        //    if (session == null)
        //        throw new InvalidOperationException("无法访问 Session 对象");

        //    // 通过反射调用 Session.SendMessage
        //    var sendMessageMethod = session.GetType().GetMethod("SendMessage", BindingFlags.NonPublic | BindingFlags.Instance);
        //    sendMessageMethod.Invoke(session, new object[] { message });
        //}








        // 调用示例
        //SendWindowChange(shell, 120, 40); // 调整为 120列 x 40行
        private void Shellstream_ErrorOccurred(object sender, Renci.SshNet.Common.ExceptionEventArgs e)
        {
            OnConsolePrint(e.Exception.Message);
            this.close();
        }

        private void Shellstream_DataReceived(object sender, Renci.SshNet.Common.ShellDataEventArgs e)
        {
            Recevice(System.Text.Encoding.UTF8.GetString(e.Data));
        }


        public SSH_Client(string IP, int port, string username, string password, bool LogAuto, string LogFile)
        {

            _IP = IP; // IP地址
            this.LogAuto = LogAuto;
            this.LogFile = LogFile;
            try
            {
                if (LogAuto)
                {
                    if (!File.Exists(LogFile))
                    {
                        CreateDir(LogFile);
                    }
                }

                ssh = new SshClient(IP, port, username, password);
                ssh.Connect();
                if (ssh.IsConnected)
                {

                    shellstream = ssh.CreateShellStream("vt100", 122, 24, 1220, 600, 4096);  //
                    shellstream.DataReceived += Shellstream_DataReceived;
                    shellstream.ErrorOccurred += Shellstream_ErrorOccurred;
                    shellstream.Closed += Shellstream_Closed;
                    //Thread read = new Thread(new ThreadStart(Receive));
                    //read.IsBackground = true;
                    //read.Start();
                }
                else
                {
                    OnConsolePrint(_IP + " ssh——链接失败");

                }
            }
            catch (Exception ex)
            {
                //   MessageBox.Show(ex.Message);
                OnConsolePrint(_IP + " " + ex.Message);

            }

        }




        /// <summary>
        /// 执行脚本时自动more
        /// </summary>
        public bool IsMore = true;
        public const string Flag_more_ZTE = "--More--";
        public const string Flag_more_H3C_HW = "---- More ----";


        public bool Waitfor(string expect)
        {
            int i = 0;
            string Temp_str1 = string.Empty;
            int last_length = 0;

            try
            {

                while (Connected)
                {
                    Thread.Sleep(1000);
                    lock (locker)
                    {
                        try
                        {
                            Temp_str1 = Str_Date_one.ToString();

                        }
                        catch (Exception e)
                        {
                            // AddError(_IP + "Telnet__Temp_str-Send(方法出现此异常):" + e.ToString() + "内容" + Temp_str1);
                        }

                    }

                    if (IsMore)
                    {
                        if (Temp_str1.TrimEnd().EndsWith(Flag_more_ZTE))
                        {
                            i = 0;
                            Thread.Sleep(100);
                            this.Send(" ");
                            continue;
                        }
                        else if (Temp_str1.TrimEnd().EndsWith(Flag_more_H3C_HW))
                        {
                            i = 0;
                            Thread.Sleep(100);
                            this.Send(" ");
                            continue;
                        }
                    }


                    if (Temp_str1.TrimEnd().EndsWith(expect))
                    {
                        return true;
                    }
                    else if (Temp_str1.Length > last_length)
                    {
                        i = 0;
                        last_length = Temp_str1.Length;
                    }
                    else if (Temp_str1.Length == last_length)
                    {
                        i++;
                        //if (i == waitfor_timeout)
                        //{
                        //  //  AddError("回显等待：" + expect + " 未匹配到,关闭连接。" + _IP);// + str_data.ToString());

                        //  //  this.Close();
                        //    break;

                        //}
                    }


                    else if (Temp_str1.Length < last_length)
                    {
                        //  AddError("当前长度小于，上次的长度，出现异常：开始打印内容");
                    }



                }

            }
            catch (Exception ex)
            {
                //  AddError("Telnet_waitfor错误" + ex.Data.ToString());
            }
            return false;
        }

        public void Send(string expect, string command, int delay_time)
        {

            int temp_int = delay_time * 1000;
            if (temp_int == 0)
            {
                temp_int = 100;
            }

            if (Waitfor(expect))
            {
                Thread.Sleep(temp_int);//执行命令的延迟
                Str_Date_one.Clear();
                Send(command);
            }
        }


        /// <summary>
        /// 取得单个命令的回显结果
        /// </summary>
        /// <param name="expect"></param>
        /// <param name="command"></param>
        /// <param name="delay_time"></param>
        /// <param name="ReadData"></param>
        /// <returns></returns>
        public bool Send(string expect, string command, int delay_time, ref string ReadData)
        {

            int temp_int = delay_time * 1000;
            if (temp_int == 0)
            {
                temp_int = 1;
            }

            int i = 0;
            string temp = string.Empty;
            while (Connected)
            {
                Thread.Sleep(1000);
                lock (Str_Date_one)
                {


                    try
                    {
                        string temps = Str_Date_one.Length.ToString();
                        temp = Str_Date_one.ToString();
                    }
                    catch (Exception e)
                    {
                        //Console.WriteLine("IP地址：" + _IP + "SSH_回显判断(方法出现此异常),错误内容：" + temp + "异常代码：" + e);
                        //string temp_log = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + "  " + _IP + " 命令：" + command + "  Telnet_send(方法出现此异常)。" + e + "\r\n";
                        //Save_log("异常LOG日志" + ".log", temp_log);


                    }

                    if (temp.ToString().TrimEnd().EndsWith(expect))
                    {
                        Thread.Sleep(temp_int);//执行命令的延迟
                        ReadData = Str_Date_one.ToString();
                        Str_Date_one.Clear();
                        Send(command);
                        return true;
                        //    break;
                    }
                    else
                    {
                        i++;
                    }

                    if (i == 30)
                    {

                        this.close();
                        ReadData = Str_Date_one.ToString();
                        return false;
                        //   break;

                    }

                }
            }
            return false;


        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expect">回显值</param>
        /// <param name="command">命令</param>
        /// <param name="delay_time">延迟</param>
        /// <param name="status">是否执行完成</param>
        public void Send(string expect, string command, int delay_time, out bool status)
        {
            status = false;
            int temp_int = delay_time * 1000;
            if (temp_int == 0)
            {
                temp_int = 1;
            }

            int i = 0;
            string temp = string.Empty;
            string temp_last = string.Empty;  // last
            while (Connected)
            {
                Thread.Sleep(1000);
                lock (locker)  // 枷锁处理
                {

                    // Console.WriteLine(Str_Date_one+"send************");
                    try
                    {
                        string temps = Str_Date_one.Length.ToString();
                        temp = Str_Date_one.ToString();
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(_IP + "SSH_Temp_str-Send(方法出现此异常)" + temp);

                        string temp_log = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + "  " + _IP + " 命令：" + command + "  SSH_send(方法出现此异常)。" + e + "\r\n";
                        Save_log("异常LOG日志" + ".log", temp_log);
                    }

                    if (temp.ToString().TrimEnd().EndsWith(expect))
                    {

                        Thread.Sleep(temp_int);//执行命令的延迟
                        Str_Date_one.Clear();
                        Send(command);
                        status = true;
                        break;

                    }
                    else if (temp.Length > temp_last.Length)
                    {
                        i = 0;
                    }
                    else
                    {
                        i++;
                    }



                    if (i == waitfor_timeout)  // 10S
                    {
                        //Console.WriteLine("命令执行错误，错误命令" + command + "命令执行结果：");// + str_data.ToString());
                        //Console.WriteLine();
                        // Console.WriteLine("命令执行错误，错误命令"+ command+"命令执行结果：" +str_data.ToString());
                        // str_data.Append("异常终结");
                        //    Console.WriteLine();
                        status = false;
                        Console.WriteLine(_IP + "回显的内容：\r\n" + temp);
                        Console.WriteLine(_IP + "回显字符：" + expect + "执行命令" + command + "没能找到回显字符，异常终结");

                        string temp_log = DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + "  " + _IP + "回显字符：" + expect + "执行命令" + command + "没能找到回显字符，异常终结" + "\r\n";
                        Save_log("异常LOG日志" + ".log", temp_log);
                        this.close();
                        break;

                    }

                }
                temp_last = temp;  //
            }


        }

        public void Send(string expect, string command, out bool status)
        {
            Send(expect, command, 1, out status);
        }

        public void Send(string expect, string command)
        {
            Send(expect, command, 1);
        }
        public void Send(string command, int delay_time)
        {
            Thread.Sleep(delay_time * 10000);
            Send(command);
        }
        public void Send(string data)
        {
            //  byte[] byteData = Encoding.ASCII.GetBytes(data);

            try
            {
                if (Connected)
                {
                    //  Send(Encoding.ASCII.GetBytes(data));
                    //  Console.WriteLine("SSH 发送数据");
                    shellstream.Write(data);
                    shellstream.Flush();
                }
            }
            catch (Exception ex)
            {
                OnConsolePrint(_IP + "发送数据异常：" + ex.ToString());
                //Console.WriteLine(_IP + "发送数据异常：" + ex.ToString()
                //    );
            }

        }

        public void Send(byte[] byteData)
        {
            try
            {
                if (Connected)
                {
                    shellstream.Write(byteData, 0, byteData.Length);
                    shellstream.Flush();
                }
            }
            catch (Exception ex)
            {
                OnConsolePrint(_IP + "发送数据异常：" + ex.ToString());

            }

        }

        /// <summary>
        /// 处理退格键盘
        /// </summary>
        /// <param name="data"></param>
        public void DeletMore(string data)
        {
            char[] temp = data.ToCharArray();
            foreach (char ch in temp)
            {
                if (ch == '\b')
                {
                    str_data.Remove(str_data.Length - 1, 1);
                }
                else
                {
                    str_data.Append(ch);
                }
            }


        }
        private void Recevice(string ResultData)
        {
            // Console.Write(ResultData);  
            lock (locker)
            {
                if (ResultData.Contains('\b'))
                {
                    DeletMore(ResultData);
                }
                else
                {
                    str_data.Append(ResultData);
                }


                Str_Date_one.Append(ResultData);
            }

            if (LogAuto)
            {
                save_log(ResultData);
            }
            OnReadData?.Invoke(null, ResultData);
            //ReadResult?.Invoke(this, ResultData);
        }




        public void close()
        {
            try
            {
                if (read != null)
                {
                    this.read.Abort();
                }

            }
            catch
            {

            }


            try
            {
                if (shellstream != null)
                {
                    shellstream.DataReceived -= Shellstream_DataReceived;
                    shellstream.ErrorOccurred -= Shellstream_ErrorOccurred;
                    shellstream.Closed -= Shellstream_Closed;
                    shellstream.Flush();
                }


            }
            catch { }

            try
            {
                if (shellstream != null)
                {
                    shellstream.Close();
                }


            }
            catch { }
            try
            {

                if (ssh != null)
                {
                    ssh.Disconnect();
                }


            }
            catch { }

            try
            {
                if (ssh != null)
                {
                    ssh.Dispose();
                }



            }
            catch { }





        }
        public void Close()
        {
            close();
        }


        /// <summary>
        /// 保存log文件
        /// </summary>
        /// <param name="FilePathName">绝对文件路径</param>
        public void Save_File(string FilePathName)
        {
            string[] Str_dir = FilePathName.Split('\\');
            string FilePath_Dir = string.Empty;
            for (int i = 0; i < Str_dir.Length - 1; i++)
            {
                FilePath_Dir += Str_dir[i] + "\\";
            }
            string FileName = Str_dir[Str_dir.Length - 1];
            Save_File(FilePath_Dir, FileName);
        }

        /// <summary>
        /// 判断文件或目录是否存在
        /// </summary>
        /// <param name="FilePath_Dir">目录</param>
        /// <param name="FileName">文件名称</param>
        private void File_Exists(string FilePath_Dir, string FileName)
        {

            string FilePathName = FilePath_Dir + "\\" + FileName;
            // string path = FilePath_Dir + "\\" + DateTime.Now.ToString("yyyy-MM-dd") + "\\" + FilePathName + ".txt";
            if (!Directory.Exists(FilePath_Dir))
            {
                Directory.CreateDirectory(FilePath_Dir);
            }

            if (!File.Exists(FilePathName))
            {
                FileStream fs = File.Create(FilePathName);
                fs.Close();
            }
        }
        /// <summary>
        /// 保存文件
        /// </summary>
        /// <param name="FilePath_Dir">文件的目录</param>
        /// <param name="FileName">文件的名称</param>
        public void Save_File(string FilePath_Dir, string FileName)//保存文件函数
        {
            string FilePathName = FilePath_Dir + "\\" + FileName;
            File_Exists(FilePath_Dir, FileName);
            StreamWriter steam = new StreamWriter(FilePathName, true, Encoding.UTF8);
            steam.Write(str_data);
            steam.Close();
        }

        private void Save_log(string FileName, string error)
        {
            LogWriteLock.EnterWriteLock();
            try
            {

                File.AppendAllText(FileName, error);
            }
            catch
            {
                OnConsolePrint("Save_log_保存error信息时，出现异常");
            }
            finally
            {
                LogWriteLock.ExitWriteLock();
            }

        }




        public static bool CreateDir(string name)
        {
            return MakeSureDirectoryPathExists(name);
        }

        public void save_log(string data)
        {
            try
            {
                lock (locker_file)
                {
                    File.AppendAllText(LogFile, data);
                }



            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

        }

    }
}
